package com.datastructure.stack;

import java.util.*;
import java.util.regex.Pattern;

public class ReversePolishMultiCalc {

    /**
     * 匹配+ - * / （ ）运算符
     * ｜代表的是或的意思
     */
    static final String SYMBOL = "\\+|-|\\*|/|\\(|\\)";

    static final String LEFT = "(";
    static final String RIGHT = ")";
    static final String ADD = "+";
    static final String MINUS = "-";
    static final String TIMES = "*";
    static final String DIVERSION = "/";

    /**
     * 加减 + -
     */
    static final int LEVEL_01 = 1;

    /**
     * 乘除
     */
    static final int LEVEL_02 = 2;

    /**
     * 括号
     */
     static final int LEVEL_HIGH = Integer.MAX_VALUE;

     static Stack<String> stack = new Stack<>();
     static List<String> data = Collections.synchronizedList(new ArrayList<>());

    /**
     * 去除所有空白符，去除空格
     */
    public static String replaceAllBlank(String s){
        return s.replaceAll("\\s+","");
    }

    /**
     * 判断是不是数字
     */
    public static boolean isNumber(String s){
        Pattern pattern = Pattern.compile("^[-\\+]?[.\\d]*$");
        return pattern.matcher(s).matches();
    }

    /**
     * 判断是不是运算符
     */
    public static boolean isSymbol(String s){
        return s.matches(SYMBOL);
    }

    /**
     * 匹配运算等级
     */
    public static int calclevel(String s){
        if("+".equals(s) || "-".equals(s)){
            return LEVEL_01;
        } else if("*".equals(s) || "/".equals(s)){
            return LEVEL_02;
        }
        return LEVEL_HIGH;
    }

    /**
     * 匹配
     */
    public static List<String> doMatch(String s) throws Exception{
        if(s == null || "".equals(s.trim())) throw new RuntimeException("data is empty");
        if(!isNumber(s.charAt(0)+"")) throw new RuntimeException("data illeagle,start not with a number");

        // 清除空格
        s = replaceAllBlank(s);

        String each;
        int start = 0;

        for(int i = 0; i < s.length(); i++){
            // 判断是不是运算符
            if(isSymbol(s.charAt(i)+"")){
                // 变为字符串
                each = s.charAt(i)+"";
                // 栈为空 左括号 优先级大于顶栈 不是加减乘除
                if(stack.isEmpty() || LEFT.equals(each) || ((calclevel(each) > calclevel(stack.peek())) && calclevel(each) < LEVEL_HIGH)){
                    stack.push(each);
                }else if(!stack.isEmpty() && calclevel(each) <= calclevel(stack.peek())){
                    // 栈为空，操作符优先级小于等于栈顶优先级时出栈入列，直到栈为空，或者遇到了(，最后操作符入栈
                    while (!stack.isEmpty() && calclevel(each) <= calclevel(stack.peek())){
                        if(calclevel(stack.peek()) == LEVEL_HIGH){
                           break;
                        }
                        data.add(stack.pop());
                    }
                    stack.push(each);
                } else if(RIGHT.equals(each)){
                    // ）操作符，一次出栈入列直到空栈或者遇到了第一个）操作符，此时）出栈
                    while(!stack.isEmpty() && LEVEL_HIGH >= calclevel(stack.peek())){
                        if(LEVEL_HIGH == calclevel(stack.peek())){
                            stack.pop();
                            break;
                        }
                        data.add(stack.pop());
                    }
                }
                start = i; // 前一个运算符的位置
            } else if(i== s.length()-1 || isSymbol(s.charAt(i+1)+"")){
                each = start == 0?s.substring(start,i+1):s.substring(start+1,i+1);
                if(isNumber(each)){
                   data.add(each);
                   continue;
                }
                throw new RuntimeException("data not match number");
            }
        }
        // 如果栈中还有元素，此时元素需要依次出栈入列，可以想像栈里剩下栈顶为/，栈底为+，应该出栈入列，可以直接翻转整个stack添加到队列
        Collections.reverse(stack);
        data.addAll(new ArrayList<>(stack));

        System.out.println(data);
        return data;
    }
    /**
     * 算出结果
     */
    public static Double doCalc(List<String> list){
        Double d = 0d;
        if(list == null || list.isEmpty()){
            return null;
        }
        if(list.size() == 1){
            System.out.println(list);
            d = Double.valueOf(list.get(0));
            return d;
        }
        ArrayList<String> list1 = new ArrayList<>();
        for(int i = 0; i < list.size(); i++){
            list1.add(list.get(i));
            if(isSymbol(list.get(i))){
                Double d1 = doTheMath(list.get(i - 2),list.get(i - 1),list.get(i));
                list1.remove(i);
                list1.remove(i-1);
                list1.set(i-2,d1+"");
                list1.addAll(list.subList(i+1,list.size()));
                break;
            }
        }
        doCalc(list1);
        return d;
    }
    /**
     * 运算
     */
    public static Double doTheMath(String s1,String s2,String symbol){
        Double result;
        switch (symbol){
            case ADD : result = Double.valueOf(s1) + Double.valueOf(s2); break;
            case MINUS: result = Double.valueOf(s1) - Double.valueOf(s2); break;
            case TIMES: result = Double.valueOf(s1) * Double.valueOf(s2); break;
            case DIVERSION: result = Double.valueOf(s1) / Double.valueOf(s2); break;
            default: result = null;
        }
        return result;
    }

    public static void main(String[] args) {
        String math = "12.8 + (2 - 3.55)*4+10/5.0";
        try {
            doCalc(doMatch(math));
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}
